--[[
	**********************
	* SvFrame - thomasfn *
	**********************
	usergroups.lua - Loads usergroup functionality
]]

-- This holds all the group data.
local groups = {}

--- Creates a group.
-- @param name The name of the group to be created.
-- @param opts You can provide a table to set custom values.
function svr.CreateGroup( name, opts )
	local n = name:lower()
	opts = opts or {}
	groups[n] = {
		Name = name,
		Title = opts.Title or "",
		Colour = opts.Colour or colour.White,
		Flags = opts.Flags or {},
		TagMode = opts.TagMode or false
	}
	return groups[n]
end

--- Deletes a group.
-- @param name The name of the group to be deleted.
function svr.DeleteGroup( name )
	groups[name:lower()] = nil
end

-- Set up the __nil group. This group always exists.
svr.CreateGroup("__nil")

local flags = {}
function svr.AddFlag( str )
	_G["FLAG_" .. str:upper() ] = str
	flags[ str:lower() ] = true
end
svr.AddFlag( "all" )

--- Loads the groups table from disk
function svr.LoadGroups()
	svr.Print( "Loading group data..." )
	local b, res = svr.FileToTable( "SvFrame/config/groups" )
	if (not b) then
		svr.Print( "Failed to load groups data! ("..res..")" )
		return
	end
	groups = res
end

-- Do the loading.
svr.LoadGroups()

--- Saves the groups table to the disk
function svr.SaveGroups()
	local b, res = svr.TableToFile( "SvFrame/config/groups", groups )
	if (not b) then
		svr.Print( "Failed to save groups data! (" .. res .. ")" )
		return
	end
end

--- Gets the group data from the 'groups' table.
function svr.GetGroup( name )
	if (groups[name:lower()]) then
		return groups[ name:lower() ]
	end
end

--- Checks if the specified group has a flag.
-- @param name The name of the group.
-- @param flag The flag to check.
function svr.GroupHasFlag( name, flag )
	local grp = groups[ name:lower() ]
	if (not grp) then return end
	return grp.Flags[ flag ] or grp.Flags[ FLAG_ALL ]
end

--- Discovers in which group the player is set to.
-- @param ply The player object.
function svr.GetPlayerGroup( ply )
	if(not ply) then svr.Print("No ply supplied!!!") end
	if (ply and ply.Group) then return ply.Group end
	local g = svr.GetPlayerPDataS( ply, "Group" ) or "__nil"
	ply.Group = g
	return g
end

--- Gets the colour by which the player's chat lines or name should appear.
-- The colour is defined in the group data and if nothing is found there it 
-- defaults to white.
function svr.GetPlayerColour( ply )
	if(not ply) then svr.Print("No ply supplied!!!") end
	local grp = svr.GetGroup( svr.GetPlayerGroup( ply ) )
	return (grp and grp.Colour) or colour.White
end

--- Assigns a player to a group.
-- @param ply The player object.
-- @param name The name of the group to assign to.
function svr.SetPlayerGroup( ply, name )
	ply.Group = name
	svr.SetPlayerPData( ply, "Group", name )
end

--- Checks if the player has a flag.
-- @param ply The player object.
-- @param flag The flag to check.
function svr.PlayerHasFlag( ply, flag )
	return svr.GroupHasFlag( svr.GetPlayerGroup( ply ), flag )
end

--- Notify the SvFrame internal hooking system about a player chatting.
-- @param ply The player object that chatted.
-- @param chat The chat string.
local function PlayerChat( ply, chat )
	
	local msg = chat:GetMessage()
	
	local result = svr.CallIHook( "player.chat", ply, msg )
	if ((not result) or (result == "")) then
		-- cancel it
		return false
	end
	
	msg = result
	
	local tag = svr.CallIHook( "player.tag", ply )
	
	svr.ChatPrint( nil, tag, COL_WHITE, ": " .. msg )
	
	return false
end
-- bind PlayerChat to the game.chat hook
hooks.Add( "game.chat", "SvFrame_Groups_Chat", PlayerChat )

--- Attach a tag to the player's chat lines.
-- @param ply The player object.
local function IPlayerTag( ply )
	local grp = svr.GetGroup( svr.GetPlayerGroup( ply ) )
	local name = ply.Username
	local colour = COL_WHITE
	if (grp) then
		if (grp.TagMode) then
			name = grp.Title .. " " .. name
		end
		colour = grp.Colour
	end
	return colour .. "^" .. name
end
-- Attach to internal hooking player.tag event as the default action.
svr.IHook( "player.tag", IPlayerTag, true )

--- Just return the chat object for now.
-- @param ply The player object.
-- @param msg The chat object.
local function IPlayerChat( ply, msg )
	return msg
end
-- Attach to internal hooking player.chat event as the default action.
svr.IHook( "player.chat", IPlayerChat, true )

--- Announce that a player has connected.
-- @param ply The player object.
local function PlayerConnect( ply )
	if (svr.GetPlayerGroup( ply ) == "__nil") then
		svr.SetPlayerGroup( ply, svr.Config:GetString( "DefaultGroup" ) or "__nil" )
	end
	svr.Print("here usergroups.lua")
	svr.ChatPrint( nil, svr.GetPlayerColour( ply ), ply.Username, colour.White, " has ", colour.Green, "joined", colour.White, " the game." )
end
-- Attach to the player.connect hook.
hooks.Add( "player.connect", "SvFrame_Groups_Connect", PlayerConnect )

--- Announce that a player has disconnected.
-- @param ply The player object.
local function PlayerDisconnect( ply )
	svr.ChatPrint( nil, svr.GetPlayerColour( ply ), ply.Username, colour.White, " has ", colour.Red, "left", colour.White, " the game." )
end
-- Attach to the player.disconnect hook.
hooks.Add( "player.disconnect", "SvFrame_Groups_Disconnect", PlayerDisconnect )

-- Let's setup the group commands.
svr.AddCommand( "group", {

	--- 
	["add"] = function( ply, args )
	
		if (not svr.CheckArg( args, 1, "string" )) then
			return ply:ChatPrint( colour.White, "Syntax: /group add <name>" )
		end
		
		if (args[1]:len() < 3) then
			return ply:ChatPrint( colour.Red, "Group name '", colour.White, args[1], colour.Red, "' is too short!" )
		end

		args[1] = args[1]:lower()
		if (svr.GetGroup( args[1] )) then
			return ply:ChatPrint( colour.Red, "A group with name '", colour.White, args[1], colour.Red, "' already exists!" )
		end
		
		local grp = svr.CreateGroup( args[1] )
		grp.Title = svr.NameToTitle( args[1] )
		grp.Flags.fake = true
		svr.SaveGroups()
		svr.ChatPrint( nil, ply.Username .. " created a new group called '", colour.Green, grp.Title, colour.White, "'!" )
	end,
	
	["del"] = function( ply, args )
		
		if (not svr.CheckArg( args, 1, "string" )) then 
			return ply:ChatPrint( colour.White, "Syntax: /group delete <name>" ) 
		end
		
		local grp = svr.GetGroup( args[1] )
		
		if (args[1] == "__nil") then
			grp = nil
		end
		
		if (not grp) then
			return ply:ChatPrint( colour.Red, "A group with name '", colour.White, args[1], colour.Red, "' does not exist!" )
		end
		
		svr.DeleteGroup( args[1] )
		svr.SaveGroups()
		svr.ChatPrint( nil, ply.Username .. " deleted the group '", grp.Colour, grp.Title, colour.White, "'!" )
	end,
	
	["list"] = function( ply, args )
		local tmp = {}
		for _, grp in pairs( groups ) do
			if (grp.Name ~= "__nil") then
				tmp[ #tmp + 1 ] = grp.Colour .. "^" .. grp.Title .. colour.White .. "^"
			end
		end
		ply:ChatPrint( "List of groups: " .. table.concat( tmp, ", " ) )
	end,
	
	["setcolour"] = function( ply, args )

		if (not svr.CheckArg( args, 1, "string" )) then
			return ply:ChatPrint( colour.White, "Syntax: /group setcolour <name> <colour>" )
		end
		if (not svr.CheckArg( args, 2, "string" )) then
			return ply:ChatPrint( colour.White, "Syntax: /group setcolour <name> <colour>" )
		end

		local grp = svr.GetGroup( args[1] )
		if (not grp) then
			return ply:ChatPrint( colour.Red, "A group with name '", colour.White, args[1], colour.Red, "' does not exist!" )
		end
		
		local col = LOOKUP_COL[ args[2] ]
		if (not col) then
			return ply:ChatPrint( colour.Red, "Invalid colour!" )
		end

		grp.Colour = col
		svr.SaveGroups()
		ply:ChatPrint( "Colour of group '", col, grp.Title, colour.White, "' updated." )
	end,
	
	["tagmode"] = function( ply, args )

		if (not svr.CheckArg( args, 1, "string" )) then
			return ply:ChatPrint( colour.White, "Syntax: /group tagmode <name> <on|off>" )
		end
		
		if (not svr.CheckArg( args, 2, "string" )) then
			return ply:ChatPrint( colour.White, "Syntax: /group tagmode <name> <on|off>" )
		end

		local grp = svr.GetGroup( args[1] )
		if (not grp) then
			return ply:ChatPrint( colour.Red, "A group with name '", colour.White, args[1], colour.Red, "' does not exist!" )
		end

		local mode = (args[2] == "on")
		grp.TagMode = mode
		svr.SaveGroups()
		ply:ChatPrint( "Tag mode of group '", grp.Colour, grp.Title, colour.White, "' updated." )

	end,
	
	["+"] = function( ply, args )
		
		if (not svr.CheckArg( args, 1, "string" )) then
			return ply:ChatPrint( colour.White, "Syntax: /group + <name> <flag>" )
		end

		if (not svr.CheckArg( args, 2, "string" )) then
			return ply:ChatPrint( colour.White, "Syntax: /group + <name> <flag>" )
		end

		local grp = svr.GetGroup( args[1] )
		if (not grp) then
			return ply:ChatPrint( colour.Red, "A group with name '", colour.White, args[1], colour.Red, "' does not exist!" )
		end
		
		if (not flags[ args[2] ]) then
			return ply:ChatPrint( colour.Red, "Invalid flag!" )
		end

		if (grp.Flags[ args[2] ]) then
			return ply:ChatPrint( colour.Red, "The group '", grp.Colour, grp.Title, colour.Red, "' already has that flag!" )
		end

		grp.Flags[ args[2] ] = true
		svr.SaveGroups()
		ply:ChatPrint( colour.Green, "Added ", colour.Teal, args[2], colour.White, " to group '", grp.Colour, grp.Title, colour.White, "'." )

	end,
	
	["-"] = function( ply, args )
		if (not svr.CheckArg( args, 1, "string" )) then return ply:ChatPrint( colour.White, "Syntax: /group - <name> <flag>" ) end
		if (not svr.CheckArg( args, 2, "string" )) then return ply:ChatPrint( colour.White, "Syntax: /group - <name> <flag>" ) end
		local grp = svr.GetGroup( args[1] )
		if (not grp) then return ply:ChatPrint( colour.Red, "A group with name '", colour.White, args[1], colour.Red, "' does not exist!" ) end
		if (not flags[ args[2] ]) then return ply:ChatPrint( colour.Red, "Invalid flag!" ) end
		if (not grp.Flags[ args[2] ]) then return ply:ChatPrint( colour.Red, "The group '", grp.Colour, grp.Title, colour.Red, "' doesn't have that flag!" ) end
		grp.Flags[ args[2] ] = false
		svr.SaveGroups()
		ply:ChatPrint( colour.Red, "Removed ", colour.Teal, args[2], colour.White, " from group '", grp.Colour, grp.Title, colour.White, "'." )
	end,
	
	["set"] = function( ply, args )
		if (not svr.CheckArg( args, 1, "string" )) then return ply:ChatPrint( colour.White, "Syntax: /group set <playername> <group>" ) end
		if (not svr.CheckArg( args, 2, "string" )) then return ply:ChatPrint( colour.White, "Syntax: /group set <playername> <group>" ) end
		local grp = svr.GetGroup( args[2] )
		if (not grp) then return ply:ChatPrint( colour.Red, "A group with name '", colour.White, args[2], colour.Red, "' does not exist!" ) end		
		local target = svr.PlayerByName( args[1], ply )
		if (not target) then return end
		svr.SetPlayerGroup( target, grp.Name )
		svr.ChatPrint( nil, target:Name() .. " is now in the group '", grp.Colour, grp.Title, colour.White, "'!" )
	end,
	
	["info"] = function( ply, args )
		if (not svr.CheckArg( args, 1, "string" )) then return ply:ChatPrint( colour.White, "Syntax: /group info <name>" ) end
		local grp = svr.GetGroup( args[1] )
		if (not grp) then return ply:ChatPrint( colour.Red, "A group with name '", colour.White, args[1], colour.Red, "' does not exist!" ) end
		local tmp = {}
		for key, b in pairs( grp.Flags ) do if (b) then tmp[ #tmp + 1 ] = key end end
		ply:ChatPrint( "Group information for ", grp.Colour, grp.Title, colour.White, ":" )
		ply:ChatPrint( "Name: " .. grp.Name )
		ply:ChatPrint( "Flags: " .. table.concat( tmp, ", " ) )
	end,
	
	["default"] = function( ply, args )

		if (not svr.CheckArg( args, 1, "string" )) then
			ply:ChatPrint( colour.White, "The default group is : "..svr.Config:GetString( "DefaultGroup" ) )
			ply:ChatPrint( colour.White, "To change it use: /group default <name>" )
			return
		end

		local grp = svr.GetGroup( args[1] )
		if (not grp) then
			return ply:ChatPrint( colour.Red, "There is no group named '", colour.White, args[1], colour.Red, "'!" )
		end

		svr.Config:SetString( "DefaultGroup", grp.Name )
		svr.Config:Save()
		ply:ChatPrint( "Default group set to ", grp.Colour, grp.Title, colour.White, "." )

	end,
	
	["flags"] = function( ply, args )
		local tmp = {}
		for k, _ in pairs( flags ) do
			tmp[ #tmp + 1 ] = colour.Teal .. "^" .. k .. colour.White .. "^"
		end
		ply:ChatPrint( "Valid flags are: " .. table.concat( tmp, ", " ) )
	end,

}, function( ply )
	if (ply.Op()) then return true end
	ply:ChatPrint( colour.Red, "You must be an op to use this command!" )
	return false
end )

svr.AddCommand( "online", function( ply, args )
	local tmp = {}
	local tmp2 = {}
	local players = players.GetAll()
	for _, pl in pairs( players ) do
		tmp2[ #tmp2 + 1 ] = svr.GetPlayerColour( pl ) .. "^" .. pl.Username .. colour.White .. "^"
		if (#tmp2 == 5) then
			tmp[ #tmp + 1 ] = tmp2
			tmp2 = {}
		end
	end
	if (#tmp2 > 0) then tmp[ #tmp + 1 ] = tmp2 end
	ply:ChatPrint( "Online players: " .. table.concat( tmp[1], ", " ) .. (((#tmp > 1) and ",") or "") )
	for i=2, #tmp do
		ply:ChatPrint( table.concat( tmp[i], ", " ) .. (((#tmp > i) and ",") or "") )
	end
end )
svr.AliasCommand( "online", "who" )
svr.AliasCommand( "online", "playerlist" )